package com.colabo.j2ee.web.core.context;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;

/**
 * Util for processing the context. Combine the spring context with the frameworks context
 * @author Anthony Do
 * @since 2009-12-23
 *
 */
public class ContextUtil {
	
	private ContextUtil() {
	}
	
	private static final String NULL_BEAN = "null";
	public static final String SCOPE_SESSION = "sessionContextHolder";
	public static final String SCOPE_REQUEST = "requestContextHolder";
	public static final String SCOPE_APPLICATION = "applicationContextHolder";
	public static final String CONTEXT_HTTP_REQUEST = "httpRequest";
	public static final String CONTEXT_HTTP_SESSION = "httpSession";
	
	private static ApplicationContext APPLICATION_CONTEXT;

	public static ApplicationContext getApplicationContext() {
		return APPLICATION_CONTEXT;
	}

	public static void setApplicationContext(ApplicationContext applicationContext) {
		ContextUtil.APPLICATION_CONTEXT = applicationContext;
	}
	
	/**
	 * Retrieve the instance of bean managed by spring context
	 * @param beanId
	 * @return instance of bean with the given name, or <code>null</code> if name is null
	 */
	public static Object getBean(String beanId) {
		if (beanId == null) {
			return null;
		}
		
		if (beanId.equalsIgnoreCase(NULL_BEAN)) {
			return null;
		}
		return getBeanByBeanIdOrClass(beanId, null);
	}
	
	/**
	 * Retrieve the bean list which is instances or ancestors'instance of the given class
	 * @param targetClass
	 * @return
	 */
	public static List<Object> getBeansByClass(Class<?> targetClass) {
		List<Object> ret = new ArrayList<Object>();
		if (APPLICATION_CONTEXT == null || targetClass == null) {
			return ret;
		}

		Map<?, ?> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(APPLICATION_CONTEXT, targetClass, true, true);

		if (beans.size() == 0) {
			return ret;
		}
		ret.addAll(beans.values());
		return ret;
	}

	/**
	 * Retrieve instance of bean according to the given id and bean class
	 * 
	 * @param beanId
	 * 				- bean id defined in config
	 * @param clazz
	 * 				- class of the bean
	 * @return instance of the bean
	 */
	public static Object getBeanByBeanIdOrClass(String beanId, Class<?> clazz) {
		if (APPLICATION_CONTEXT == null) {
			return null;
		}

		if (beanId.equalsIgnoreCase(NULL_BEAN)) {
			return null;
		}

		if (beanId != null) {
			if (APPLICATION_CONTEXT.containsBean(beanId)) {
				return APPLICATION_CONTEXT.getBean(beanId);
			}
		}

		List<Object> l = getBeansByClass(clazz);
		if (l.size() > 0) { // return the first instance of the class
			return l.get(0);
		}
		return null;
	}

	/**
	 * Store the bean instance in the data holder in context
	 * @param key 
	 * 			- a string key to the bean
	 * @param value
	 * 			- instance object of the bean
	 * @param scopeBeanId
	 * 			- scope of the bean
	 */
	public static void put(String key, Object value, String scopeBeanId) {
		ContextBeanHolder contextHolder = (ContextBeanHolder) getBean(scopeBeanId);
		if (contextHolder != null) {
			contextHolder.put(key, value);
		}
	}

	/**
	 * Retrieve the bean from the data holder in context
	 * @param key 
	 * 			- a string key to the bean
	 * @param scopeBeanId
	 * 			- scope of the bean
	 * @return instance object of the bean
	 */
	public static Object get(String key, String scopeBeanId) {
		ContextBeanHolder contextHolder = (ContextBeanHolder) getBean(scopeBeanId);
		if (contextHolder != null) {
			return contextHolder.get(key);
		}
		return null;
	}

	/**
	 * Clear the data holder against the given scope
	 * @param scopeBeanId 
	 * 				- scope of the bean
	 */
	public static void clear(String scopeBeanId) {
		ContextBeanHolder contextHolder = (ContextBeanHolder) getBean(scopeBeanId);
		if (contextHolder != null) {
			contextHolder.clear();
		}
	}

}
